// padding 应该对了

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <fstream>
#include <istream>
#include <cstring>

#include <openssl/aes.h>
#include <openssl/crypto.h>
#include <openssl/evp.h>

const int KEY_SIZE = 16;   //  128bit

#define LOG(x) std::cout << (x) << std::endl;


bool encrypt_file(const char* infile, const char* outfile, const char* key, int key_size)
{
	unsigned char inbuf[4096];
	unsigned char outbuf[4096];
	unsigned char user_key[KEY_SIZE];
	AES_KEY encrypt_key = {};
	size_t offset = 0;
	size_t len = 0;
	int outlen = 0;
	strncpy((char*)user_key, key, KEY_SIZE);

	std::ifstream in(infile, std::ios::binary);
	std::ofstream out(outfile, std::ios::binary);
	if (in.fail() || out.fail())
		return false;
	
	AES_set_encrypt_key(user_key, KEY_SIZE * 8, &encrypt_key);

	while (true)
	{
        len = sizeof(inbuf) -AES_BLOCK_SIZE;

		in.read((char *)inbuf, len);
		len = in.gcount();
		if (len == 0)
			break;
        
		outlen = len;
		if (in.eof())  // file eof need padding
		{
            outlen = (outlen / AES_BLOCK_SIZE + 1) * AES_BLOCK_SIZE;
			memset(inbuf + len, (outlen - len), outlen - len);  // padding
		}
		
		offset = 0;
		for (int i = 0; i < outlen / AES_BLOCK_SIZE; ++i)
		{
			AES_ecb_encrypt(inbuf + offset, outbuf + offset, &encrypt_key, AES_ENCRYPT);
			offset += AES_BLOCK_SIZE;
		}
		
		out.write((char*)outbuf, outlen);
	}

	in.close();
	out.flush();
	out.close();
	return true;
}



bool decrypt_file(const char* infile, const char* outfile, const char* key, int key_size)
{
	unsigned char inbuf[4096];
	unsigned char outbuf[4096];
	unsigned char user_key[KEY_SIZE];
	AES_KEY decrypt_key = {};
	size_t offset = 0;
	size_t len = 0;
	int outlen = 0;

	strncpy((char*)user_key, key, KEY_SIZE);

	std::ifstream in(infile, std::ios::binary);
	std::ofstream out(outfile, std::ios::binary);
	if (in.fail() || out.fail())
		return false;

	AES_set_decrypt_key(user_key, KEY_SIZE * 8, &decrypt_key);
	while (true)
	{
		in.read((char *)inbuf, sizeof(inbuf));
		len = in.gcount();
		if (len == 0)
			break;

		offset = 0;
		for (int i = 0; i < len / AES_BLOCK_SIZE; ++i)
		{
			AES_ecb_encrypt(inbuf + offset, outbuf + offset, &decrypt_key, AES_DECRYPT);
			offset += AES_BLOCK_SIZE;
		}

		outlen = len;

		if (in.eof())  // parser padding
		{
			int padding = (int)outbuf[outlen - 1];
			for (int j = 0; j < padding; ++j)
			{
				if ((int)outbuf[outlen - j - 1] != padding)
				{
					padding = 0;
					break;
				}
			}
			outlen -= padding;
		}

		out.write((char*)outbuf, outlen);
	}

	in.close();
	out.flush();
	out.close();
	return true;
}


int main(int argc, const char* argv[])
{
	OpenSSL_add_all_algorithms();

	const char* key = "1234567890123456";
	encrypt_file("input.mp3", "encrypt.emp3", key, strlen(key));

	decrypt_file("encrypt.emp3", "output.mp3", key, strlen(key));
	return 0;
}

